/****************************************************************************************
 * File:			Controller.cs
 * Description:		Controller that manages all the FEAR animats, connects and disconnects 
 *					them, and provides the basic implementation for more advanced 
 *					controller types 	like tournament managers.
 *                  Basic handling of the bots within the game.
 ***************************************************************************************/

using System;
using System.Collections;
using System.Collections.Generic;
using System.Text;
using System.IO;

namespace XGAE.Framework
{
    /// <summary>
    /// Management for the animats in the game.  Provides storage for the instances that have 
    /// been loaded into the engine, and basic processing.
    /// </summary>
    class Controller : IDisposable
    {
        #region Fields

        private Dictionary<Component, S_Container> population; // list of animats present in the game
        private Queue<string> queue; // animats to be inserted into the game

        private struct S_Container
        {
            private Animat animat;

            public Animat Animat
            {
                get { return animat; }
                set { animat = value; }
            }

            private int errors;

            public int Errors
            {
                get { return this.errors; }
                set { this.errors = value; }
            }

            /*public S_Container()
            {
                this.animat = null;
                this.errors = 0;
            }*/

            public int IncrimentErrors()
            {
                return ++errors;
            }

            public void SetAnimat(Animat animat)
            {
                this.animat = animat;
            }
        }

        #endregion

        #region C'tors

        /// <summary>
        /// Default constructor.
        /// </summary>
        public Controller()
        {
            string path2Modules = @"fear/modules/";

            try
            {
                int modulesCount = 0;
                DirectoryInfo dirInfo = new DirectoryInfo(path2Modules);                
                FileInfo[] modules = dirInfo.GetFiles("*.dll", SearchOption.TopDirectoryOnly);

                foreach (FileInfo m in modules)
                {
                    string name = m.Name;
                    name.Replace(m.Extension, string.Empty);

                    DllFactory dllFactory = new DllFactory(m.FullName, name);

                    ++modulesCount;
                }

                //cout << modulesCount << " modules found" << endl;
            }
            catch (Exception ex)
            {
                //cerr << "Exception while scanning for modules." << endl;
            }
        }

        /*/// <summary>
        /// Default virtual destructor.
        /// </summary>
        public virtual ~Controller()
        {
            foreach(S_Container s_c in population)
            {
                s_c.Animat = null;
            }
        }*/

        #region IDisposable Methods

        // Track whether Dispose has been called.
        private bool disposed = false;

        // Implement IDisposable.
        // Do not make this method virtual.
        // A derived class should not be able to override this method.
        public void Dispose()
        {
            Dispose(true);
            // This object will be cleaned up by the Dispose method.
            // Therefore, you should call GC.SupressFinalize to
            // take this object off the finalization queue
            // and prevent finalization code for this object
            // from executing a second time.
            GC.SuppressFinalize(this);
        }

        // Dispose(bool disposing) executes in two distinct scenarios.
        // If disposing equals true, the method has been called directly
        // or indirectly by a user's code. Managed and unmanaged resources
        // can be disposed.
        // If disposing equals false, the method has been called by the
        // runtime from inside the finalizer and you should not reference
        // other objects. Only unmanaged resources can be disposed.
        protected virtual void Dispose(bool disposing)
        {
            // Check to see if Dispose has already been called.
            if (!this.disposed)
            {
                // If disposing equals true, dispose all managed
                // and unmanaged resources.
                if (disposing)
                {
                    // Dispose managed resources.
                    foreach (KeyValuePair<Component, S_Container> kvp in population)
                    {
                        kvp.Value.SetAnimat(null);
                    }
                }

                // Note disposing has been done.
                disposed = true;
            }
        }

        // Use C# destructor syntax for finalization code.
        // This destructor will run only if the Dispose method
        // does not get called.
        // It gives your base class the opportunity to finalize.
        // Do not provide destructors in types derived from this class.
        ~Controller()
        {
            // Do not re-create Dispose clean-up code here.
            // Calling Dispose(false) is optimal in terms of
            // readability and maintainability.
            Dispose(false);
        }

        #endregion

        #endregion

        #region Public Methods

        /// <summary>
        /// Handle a single frame of environment simulation.  This just inserts and removes animats
        /// where needed.
        /// </summary>
        public void Run()
        {
            if (queue.Count > 0)
            {
                Insert(queue.Dequeue());
            }
        }

        /// <summary>
        /// Let a single bot think for this frame.
        /// </summary>
        /// <param name="body">The body of the creature to process.</param>
        public void Process(Component body)
	    {
            // try to locate this animat in the population
            if (!population.ContainsKey(body))
            {
                //Warning("Cannot process animat; not found in population.");
                return;
            }
            // call the Think() function, and check if there are problems
            if (!population[body].Animat.Exist())
            {
                // remove after three warnings
                if (population[body].IncrimentErrors() >= 3)
                {
                    //Warning("Animat removed from population after three exceptions.");
                    this.Disconnect(body);
                }
            }
	    }

        /// <summary>
        /// Free and remove a specific animat from the game.
        /// </summary>
        /// <param name="body">Reference to the body of the creature to disconnect.</param>
        public void Disconnect(Component body)
        {
            if (body == null)
            {
                IEnumerator<KeyValuePair<Component, S_Container>> enumerator =
                    (IEnumerator<KeyValuePair<Component, S_Container>>)population.GetEnumerator();
                enumerator.Reset();
                body = enumerator.Current.Key;
            }

            if (!population.ContainsKey(body))
            {
                //Warning("Can't disconnect animat; not found in population.");
                return;
            }
            
            population[body].Animat.Save();
            population[body].SetAnimat(null);
            population.Remove(body);
            Factory.Delete(body);
        }

        /// <summary>
        /// Put a specific animat into the connection queue.
        /// </summary>
        /// <param name="name">Nickname of the animat to be loaded.</param>
        public void Connect(string name)
        {
            if (name == string.Empty)
                //queue.push_back( SelectRandom() );
                //Warning("Refusing to add random animat.  Use 'sv add' instead.");
                ;
            else
                queue.Enqueue(name);
        }

        #endregion

        #region Private Methods

        /// <summary>
        /// Load an animat from its directory into the game.
        /// </summary>
        /// <param name="name"></param>
        private void Insert(string name)
        {
            /*string appearance, gender*/;
	        Component body;

	        try
	        {
		        body = Factory.Create("Backend");
	        }
	        catch(/*Framework*/Exception ex)
	        {
		        //std::cerr << e.GetMessage() << endl;
		        return;
	        }

            Animat animat = this.Request( name, body );

            if (animat != null)
	        {
		        Factory.Delete( body );
		        return;
	        }

            // insert into world, and store for later
	        population[body].SetAnimat(animat);
            // initialise body to finish off
	        body.Init();
        }

        /// <summary>
        /// Scan the stables subdirectory for a list of animats, and pick one randomly.
        /// </summary>
        /// <returns></returns>
        private string SelectRandom()
        {
	        /*assert( false );
            //

            FIXME: select random must scan the stables, not the factory registry

	            std::vector<std::string> v = Factory<Brain>::GetInstance().GetVector();
	            if (v.size())
		            return v[rand(v.size())];
	            else
            //*/
	        return string.Empty;
        }

        /// <summary>
        /// Find and prepare a particular animat.
        /// </summary>
        /// <param name="name">Nickname of the creature to be loaded.</param>
        /// <param name="body">Body that has been allocated for this animat.</param>
        /// <returns>Reference to the newly allocated animat.</returns>
        private Animat Request(string name, Component body)
        {
            Animat animat = null;

            try
	        {
                animat = new Animat(name, body);
                animat.Load();
	        }
	        catch (Exception ex)
	        {
		        //printf( "Exception while loading animat %s.\n%s\n", name.c_str(), e.GetMessage().c_str() );
                animat = null;
	        }

            return animat;
        }

        #endregion
    }
}
